package GameState.Ships;

import java.awt.Point;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import GameState.GamePiece;
import GameState.GamePieceType;
import GameState.Projectiles.CannonBall;


public class PirateShip extends GamePiece{

	/**
	 * 
	 */
	private static final long serialVersionUID = -7928827157754672864L;
	//private static int GRAPHICS_WIDTH ; 
	//private static int GRAPHICS_HEIGHT ;
	//private final static int BOAT_LENGHT = 305;
	private final static int BOAT_WIDTH = 100;
	private final static int MOVE_CONSTANT = 5;
	private final static int MAX_AMMO = 24;
	private static final int CREATE_DIST = 5;
	private static int RAN_CHANCE_RELOAD = 50;
	private static final int MAX_SPEED = 3;


	/*
	 * Boat Measurements
	 */
	private final static int Y_DIST_FROM_CENTER_TO_TOP = 140;

	private final static int X_DIST_FROM_CENTER_TO_BASECURVE = 30;
	private final static int Y_DIST_FROM_CENTER_TO_BASECURVE = 90;

	private final static int X_DIST_FROM_CENTER_TO_JUT_OUT_BIT = 40;
	private final static int Y_DIST_FROM_CENTER_TO_JUT_OUT_BIT= 10;

	private final static int X_DIST_FROM_CENTER_TO_BOTTOM = 20;
	private final static int Y_DIST_FROM_CENTER_TO_BOTTOM= 135;

	private final static int Y_DIST_FROM_CENTER_TO_VERY_BOTTOM= 140;

	private final static int SHOOTABLE_AREA = 100;
	//	private final static int SHIP_LIMIT_SPEED = 10; 
	//	private final double SHIP_ACCERATION = .02f;
	//	private final  double SHIP_DEACCERATION = .01f;
	//	private final static double ANCHOR_DEACCERATION = 0.05;
	private final static int GHOST_TIME = 300;


	//private final String mImagePath = "pirate_ship_wshadow_SV.png";
	//private BufferedImage mImage;
	private int mDirection;
	private int mXposition;
	private int mYposition;
	//	private double mSpeed = 0;
	private double mYvelocity = 0;
	private double mXvelocity = 0;




	private int mHealth;
	private int mLeftAmmo;
	private int mRightAmmo;
	private String mPlayerName;



	//private final static Shape SHIP_SHAPE = new Shape();

	//	private int shipHeight = 100;
	//	private int shipWidth =  50;
	//	private int shipBowHieght = 50;
	private Random mRan;
	private int mMultiplier = 1;
	private int mPlayerScore;

	private int mGhostShipTime;


	private boolean anchoreOn; 

	/**
	 * 
	 * Player requests
	 * 
	 */
	//private RequestsTypes[] requests = new RequestsTypes[6];


	/*private int randomAmount(final boolean isX){


		if(isX){

			System.out.println((int)(myRan.nextInt() * Math.sin(Math.toRadians(mDirection))));

			return  (int)((myRan.nextInt(BOAT_LENGHT/2) - BOAT_LENGHT) * Math.sin(Math.toRadians(mDirection)));

		}else{

			return  (int)((myRan.nextInt(BOAT_LENGHT/2) - BOAT_LENGHT) * Math.cos(Math.toRadians(mDirection)));

		}




	}*/

	public PirateShip()  {

		mDirection = 0;
		mXposition = 50;
		mYposition = 50;

		mHealth = 50;
		mLeftAmmo = MAX_AMMO;
		mRightAmmo = MAX_AMMO;

		mRan = new Random();
		//mImage = null;

		mPlayerScore = 0;
		mMultiplier = 1;

		try {

			//	mImage = ImageIO.read( new File(mImagePath));

		}catch(final Exception ex){

			ex.printStackTrace();

		}
		//GRAPHICS_WIDTH = mImage.getWidth();
		//GRAPHICS_HEIGHT = mImage.getHeight();
	}

	public void respawn()   {

		mDirection = mRan.nextInt(360);
		mXposition = mRan.nextInt(200);
		mYposition = mRan.nextInt(200);
		mGhostShipTime = GHOST_TIME;
		mHealth = 50;

	}

	public boolean isGhostShip()   {

		return mGhostShipTime > 0;

	}	

	public int getHealth()   {

		return mHealth;

	}

	public synchronized void updateAmmo()  {



		if(mRightAmmo < MAX_AMMO && mRan.nextInt(RAN_CHANCE_RELOAD) == 0){
			mRightAmmo++;
		}

		if(mLeftAmmo < MAX_AMMO && mRan.nextInt(RAN_CHANCE_RELOAD) == 0 ){
			mLeftAmmo++;
		}


	}


	/* ******************************************************************************************************
	 *  									Getter Setter methods											*
	 ****************************************************************************************************** */
	public double getXvel()   {

		return mXvelocity;
	}

	public double getYvel()   {

		return mYvelocity;

	}


	public double getCalXvelChange()   {

		return Math.sin(Math.toRadians(mDirection));

	}

	public double getCalYvelChange()   {

		return -Math.cos(Math.toRadians(mDirection));

	}

	public String getName()   {

		if(mPlayerName == null){

			return "unknown";

		}

		return mPlayerName;

	}

	public int getScore()   
	{
		return mPlayerScore;

	}

	public void setScore(int score)   
	{
		mPlayerScore = score;	
	}

	public int getMultipiler()   
	{
		return mMultiplier;	
	}

	public void incMultipiler()   {

		++mMultiplier;
	}

	public void resetMultiplier()   {

		mMultiplier = 1;
	}

	public int getRightAmoo()   {

		return mRightAmmo;

	}

	public  int getLeftAmoo()    {

		return mLeftAmmo;

	}

	public int getDirection()   {

		return mDirection;

	}

	public int getY()   {

		return mYposition;

	}

	public int getX()   {

		return mXposition;

	}



	public void turnLeft()  {

		mDirection -= MOVE_CONSTANT;

		if(mDirection < 0){

			mDirection += 360; 

		}
	}	

	/*
	 * 
	 * inomine1
	 * 
	 */
	public void turnRight()  {


		mDirection += MOVE_CONSTANT;

		if(mDirection >= 360){

			mDirection -= 360; 

		}



	}

	public void slowDown()  {

		mYvelocity /= 1.02;
		mXvelocity /= 1.02;

		if (Math.abs(mYvelocity) < 0.01){

			mYvelocity = 0;
		}

		if (Math.abs(mXvelocity) < 0.01){

			mXvelocity = 0;
		}



	}

	public void moveForward()  {

		//changeSpeed(SHIP_ACCERATION);
		changeSpeed(0);
	}

	/**
	 * 
	 * gives addition accleration or deacceration based on the direction of the wind
	 * 
	 * @param theWind
	 *
	public void windShip(Wind theWind){

		addSpeed( theWind.getAcceration(mDirection));


	}
	 */
	public void worldWrap(int upperbound, int lowerbound){


		if (mXposition > upperbound){

			mXposition  = lowerbound;

		} else if ( mXposition < lowerbound){

			mXposition = upperbound;

		}


		if (mYposition > upperbound){

			mYposition  = lowerbound;

		} else if (mYposition < lowerbound){

			mYposition = upperbound;

		}


	}

	private synchronized void changeSpeed(double acceleration)  {

		/* double speed = mSpeed + d;
		if (speed > SHIP_LIMIT_SPEED){

			speed = SHIP_LIMIT_SPEED;

		} else if (speed < 0 ){

			mSpeed = 0;

		 } else{

			 mSpeed =  speed;
			 TODO

		 }*/

		mXvelocity += getCalXvelChange()  * 2;
		mYvelocity += getCalYvelChange()  * 2;


		if ( Math.abs(mXvelocity) > MAX_SPEED){

			if (mXvelocity >= 0){
				mXvelocity = MAX_SPEED;
			} else {
				mXvelocity = -MAX_SPEED;
			}
		}
		if (Math.abs(mYvelocity) >= Math.abs(MAX_SPEED)){
			if( mYvelocity >= 0){
				mYvelocity = MAX_SPEED;
			} else {
				mYvelocity = -MAX_SPEED;

			}
		}



	}

	public synchronized void moveShip()  {


		/*addSpeed(-SHIP_DEACCERATION);
		int shipMovementX = (int) (shipMoveX()   * mSpeed);
		int shipMovementY = (int) (shipMoveY()   * mSpeed);
		mXposition +=  shipMovementX;
		mYposition -= shipMovementY;

mXposition
		if(anchoreOn){
			addSpeed(-ANCHOR_DEACCERATION);	
			if (mSpeed == 0){

				anchoreOn  =false;

			}
		}

		if(isGhostShip()  ){


			mGhostShipTime--;
		}*/


		mXposition += mXvelocity;
		mYposition += mYvelocity;

	}

	//	private synchronized int shipMoveX()  {
	//
	//		return (int) (mSpeed * Math.sin(Math.toRadians(mDirection)));
	//
	//	}
	//
	//	private synchronized int shipMoveY()  {
	//
	//		return (int) (mSpeed * Math.cos(Math.toRadians(mDirection)));
	//
	//	}

	public synchronized void hit(final int damageTaken)  {

		mHealth -= damageTaken;


		if(mHealth <= 0){


			//causes the ships to be destroyed
			// and spawns it some where else.
			destroy();

		}

	}


	public void destroy()  {
		//reduces all the multipliers ect.


		//spawns the ship on the map
		respawn();


	}


	public boolean getAnchorOn()  {

		return 	anchoreOn;

	}

	public synchronized List<CannonBall> shootLeft()  {

		List<CannonBall> cannonShots  = new LinkedList<CannonBall>();

		Point p = rotate( new Point(
										-BOAT_WIDTH/2 - CREATE_DIST,
										mRan.nextInt(SHOOTABLE_AREA) - SHOOTABLE_AREA/2
									)
		); 		

		if( mLeftAmmo  > 0){

			mLeftAmmo--;

			cannonShots.add( new CannonBall(mDirection - 90,
					mXposition + p.x, mYposition + p.y ));


		}


		return cannonShots;

	}

	public synchronized List<CannonBall> shootRight(){

		List<CannonBall> cannonShots  = new LinkedList<CannonBall>();

		Point p = rotate(  new Point(BOAT_WIDTH/2 + CREATE_DIST, mRan.nextInt(SHOOTABLE_AREA) - SHOOTABLE_AREA/2)); 

		if( mRightAmmo  > 0){

			mRightAmmo--;

			cannonShots.add( new CannonBall(mDirection + 90,
					mXposition + p.x, mYposition + p.y));
		}

		return cannonShots;
	}



	private boolean pointOnCorrectSideOfLine(int x,  int y,  int x0, int  y0, int  x1, int  y1 ){


		if( (y - y0) * (x1 - x0) - (x - x0)*(y1 - y0) > 0){

			return true;

		}

		return false;

	}

	public Point[] createTheXYArray() {

		Point [] XY =  new Point[8];

		/*XY[0][0] = -BOAT_WIDTH/2 + 20;
		XY[0][1] = -BOAT_LENGHT/2 + 50;

		XY[1][0] = 0;
		XY[1][1] = -BOAT_LENGHT/2 - 5;

		XY[2][0] = BOAT_WIDTH/2 - 20;
		XY[2][1] = -BOAT_LENGHT/2  + 50;

		XY[3][0] = BOAT_WIDTH/2 - 25;
		XY[3][1] = BOAT_LENGHT/2 - 10;

		XY[4][0] = - BOAT_WIDTH/2 + 25;
		XY[4][1] = BOAT_LENGHT/2 - 10 ;
		 *
		 *
		 * Point which make up th hit box:S

		        0
		       /\
		      /  \ 
		 *   /    \
		 * 7/      \ 1
		 *  |      |
		 *  |      |
		 *  |      |
		 * 6|      |2
		 *   |    |
		 *   |    |
		 *   |    |
		 *  5|_  _|3
		 *     ..
		 *      4
		 * 
		 */





		XY[0] = new Point(0, Y_DIST_FROM_CENTER_TO_TOP);
		//XY[0][1] = -Y_DIST_FROM_CENTER_TO_TOP;

		XY[1] = new Point( X_DIST_FROM_CENTER_TO_BASECURVE, Y_DIST_FROM_CENTER_TO_BASECURVE);
		//XY[1][1] = -Y_DIST_FROM_CENTER_TO_BASECURVE;

		XY[2] = new Point( X_DIST_FROM_CENTER_TO_JUT_OUT_BIT, Y_DIST_FROM_CENTER_TO_JUT_OUT_BIT);
		//XY[2][1] = Y_DIST_FROM_CENTER_TO_JUT_OUT_BIT;

		XY[3] = new Point( X_DIST_FROM_CENTER_TO_BOTTOM, Y_DIST_FROM_CENTER_TO_BOTTOM);
		//XY[3][1] = Y_DIST_FROM_CENTER_TO_BOTTOM;

		XY[4] =  new Point(0, Y_DIST_FROM_CENTER_TO_VERY_BOTTOM);
		//XY[4][1] = Y_DIST_FROM_CENTER_TO_VERY_BOTTOM;

		XY[5] =  new Point(-X_DIST_FROM_CENTER_TO_BOTTOM, Y_DIST_FROM_CENTER_TO_BOTTOM);
		//XY[5][1] = Y_DIST_FROM_CENTER_TO_BOTTOM;

		XY[6] =  new Point(-X_DIST_FROM_CENTER_TO_JUT_OUT_BIT, Y_DIST_FROM_CENTER_TO_JUT_OUT_BIT);
		//XY[6][1] = Y_DIST_FROM_CENTER_TO_JUT_OUT_BIT;

		XY[7] = new Point(-X_DIST_FROM_CENTER_TO_BASECURVE, -Y_DIST_FROM_CENTER_TO_BASECURVE);
		//XY[7][0] = -X_DIST_FROM_CENTER_TO_BASECURVE;
		//XY[7][1] = -Y_DIST_FROM_CENTER_TO_BASECURVE;

		//does the rotating and positioning normalzation
		for(int count = 0 ; count < XY.length; count++){


			XY[count] = rotate(XY[count]);
			XY[count].x += mXposition; 
			XY[count].y += mYposition;


		}


		return XY;
	}

	/**
	 * 
	 * Uses rotation formula :
	 *  
	 *  
	 *  X = x cos E - y sin E
	 *
	 *Y =  x sin E + y cos E
	 * 
	 * rotates the point.
	 * 
	 */
	private Point rotate(Point p){

		Point newPoint = new Point(
				(int) ( p.x * Math.cos(Math.toRadians(mDirection)) - p.y * Math.sin(Math.toRadians(mDirection))),
				(int)( p.x * Math.sin(Math.toRadians(mDirection)) + p.y * Math.cos(Math.toRadians(mDirection)))
		);



		return newPoint;
	}

	/*
	 * Collision Detection for everything that a pirateship can encounter
	 */
	public boolean collide(PirateShip s) {

		if(this.isGhostShip() || s.isGhostShip())
			return false;


		Point [] ShipOutLinePoints =  s.createTheXYArray();
		
		
		for(Point OtherShipPoint : ShipOutLinePoints){
			
			if (checkPointCollide(OtherShipPoint))
				return true;
			
			
		}

			return false;
		
	}


	public boolean collide(CannonBall ball) {


		//checks that this ship is not a ghost ship
		if(this.isGhostShip())
			return false;

		return checkPointCollide(ball.getPosition());

	}

	private boolean checkPointCollide(Point collisionPoint) 
	{


		Point [] XY = createTheXYArray();



		for ( int count = 0 ; count < XY.length -1; count++)
		{
			if(!pointOnCorrectSideOfLine(collisionPoint.x, collisionPoint.y, XY[count].x, XY[count].y, XY[count+ 1].x, XY[count + 1].y)){

				return false;

			}
		}

		//do the last check by hand -- This is comparing the first and last points
		if(!pointOnCorrectSideOfLine(collisionPoint.x, collisionPoint.y, XY[XY.length -1 ].x, XY[XY.length - 1].y, XY[0].x, XY[0].y)){

			return false;

		}


		System.out.println("collide");
		return true;


	}


	public Point getPosition() {
		return new Point(mXposition, mYposition);
	}


	public GamePieceType getRenderType() {
		return GamePieceType.SHIP;
	}


	public int getRotation() {
		return mDirection;
	}


}
